#ifndef __TWI_H__
#define __TWI_H__

#include <ch32v_def.h>
#include "PeripheralPins.h"
#ifdef __cplusplus
extern "C"
{
#endif

/* Exported types ------------------------------------------------------------*/
/* offsetof is a gcc built-in function, this is the manual implementation */
#define OFFSETOF(type, member) ((uint32_t)(&(((type *)(0))->member)))

/* Interrupt priority */
#ifndef I2C_IRQ_PRIO
#define I2C_IRQ_PRIO 2
#endif
#ifndef I2C_IRQ_SUBPRIO
#define I2C_IRQ_SUBPRIO 0
#endif

/* I2C Tx/Rx buffer size */
#if !defined(I2C_TXRX_BUFFER_SIZE)
#define I2C_TXRX_BUFFER_SIZE 32
#elif (I2C_TXRX_BUFFER_SIZE >= 256)
#error I2C buffer size cannot exceed 255
#endif

    typedef struct i2c_s i2c_t;

    struct i2c_s
    {
        I2C_TypeDef *i2c;
        I2C_InitTypeDef typeDef;
        void *__this;
        PinName sda;
        PinName scl;
        IRQn_Type irq;
        IRQn_Type irqER;
        volatile int slaveRxNbData; // Number of accumulated bytes received in Slave mode
        volatile int slaveTxNbData;
        void (*i2c_onSlaveReceive)(i2c_t *);
        void (*i2c_onSlaveTransmit)(i2c_t *);
        volatile uint8_t i2cTxRxBuffer[I2C_TXRX_BUFFER_SIZE];
        volatile uint8_t i2cTxRxBufferSize;
        volatile uint8_t slaveMode;
        uint8_t isMaster;
        uint8_t generalCall;
    };

    ///@brief I2C state
    typedef enum
    {
        I2C_OK = 0,
        I2C_DATA_TOO_LONG = 1,
        I2C_NACK_ADDR = 2,
        I2C_NACK_DATA = 3,
        I2C_ERROR = 4,
        I2C_TIMEOUT = 5,
        I2C_BUSY = 6
    } i2c_status_e;

    /* Exported functions ------------------------------------------------------- */
    void i2c_init(i2c_t *obj);
    void i2c_custom_init(i2c_t *obj, uint32_t timing, uint32_t addressingMode,
                         uint32_t ownAddress);
    void i2c_deinit(i2c_t *obj);
    void i2c_setTiming(i2c_t *obj, uint32_t frequency);
    i2c_status_e i2c_master_write(i2c_t *obj, uint8_t dev_address, uint8_t *data, uint16_t size);
    i2c_status_e i2c_slave_write_IT(i2c_t *obj, uint8_t *data, uint16_t size);
    i2c_status_e i2c_master_read(i2c_t *obj, uint8_t dev_address, uint8_t *data, uint16_t size);

    i2c_status_e i2c_IsDeviceReady(i2c_t *obj, uint8_t devAddr, uint32_t trials);

    void i2c_attachSlaveRxEvent(i2c_t *obj, void (*function)(i2c_t *));
    void i2c_attachSlaveTxEvent(i2c_t *obj, void (*function)(i2c_t *));
#ifdef __cplusplus
}
#endif

#endif // __TWI_H__